package org.vossnet.sfig;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import static java.lang.String.format;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import org.apache.log4j.Logger;

import static org.vossnet.sfig.RunFig.refineValue;

/**
 * If SFig is to be used in conjunction to the Spring-Framework, then
 * this special helper class provides a suitable implementation of the parser
 * helper methods setObjectProperty() and newInstance(). RunFig then relies
 * on the Spring-Framework for bean factory capability.
 * 
 * <p>NOTE: Once the Spring application context is initialized, then the RunFig
 * context is discarded (i.e., the RunFig.close() method will be called).
 * 
 * <p>The target.spring.framework property must be defined as "true" at compile
 * time when building RunFig in order to select this implementation.
 * 
 * @see RunFig#setObjectProperty(Object, String, Object)
 * @see RunFig#newInstance(String, List)
 * @see RunFig#close()
 * 
 * @author rogerv
 */
public final class SpringBeanDefServices extends BeanDefServices {
    public SpringBeanDefServices(Logger logger) { super(logger); }

    @Override
    protected Class<?> getClass(Object o) {
        return ((AbstractBeanDefinition) o).getBeanClass();
    }

    @Override
    protected void setProperty(String propName,Method m, Object o, Class<?> t, Object value)
        throws IllegalAccessException, InvocationTargetException {
        ((BeanDefinition) o).getPropertyValues().addPropertyValue( propName, refineValue(t.getName(),value) );
    }

    @Override
    protected void setField(Field f, Object o, Object value)
        throws IllegalAccessException, NoSuchFieldException {
        ((BeanDefinition) o).getPropertyValues().addPropertyValue( f.getName(), refineValue(f.getType().getName(),value) );
    }

    @Override
    protected Object newInstance(Class<?> cls,Class<?> rtnType) throws InstantiationException, IllegalAccessException {
        if (log.isDebugEnabled())
            log.debug(format("Requested new instance of class %s, using default constructor",cls));
        if (rtnType.getSimpleName().equalsIgnoreCase("Object")) {
            final GenericBeanDefinition beanDef = new GenericBeanDefinition();
            beanDef.setBeanClass(cls);
            return beanDef;
        } else {
            return super.newInstance(cls, rtnType);
        }
    }

    @Override
    protected Object newInstance(Class<?> cls,Constructor<?> ctor,List<Object> ctorArgs,Class<?> rtnType)
        throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
        if (log.isDebugEnabled())
            log.debug(format("Requested new instance of class %s, passing %d constructor args",cls,ctorArgs.size()));
        if (rtnType.getSimpleName().equalsIgnoreCase("Object")) {
            final ConstructorArgumentValues ctorArgVals = new ConstructorArgumentValues();
            int i = 0;
            for(final Object argVal : ctorArgs) {
                ctorArgVals.addIndexedArgumentValue(i++, argVal);
            }
            final GenericBeanDefinition beanDef = new GenericBeanDefinition();
            beanDef.setBeanClass(cls);
            beanDef.setConstructorArgumentValues(ctorArgVals);
            return beanDef;
        } else {
            return super.newInstance(cls, ctor, ctorArgs, rtnType);
        }
    }

}
